﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Diagnostics;
using System.Windows.Media;

namespace Master
{
    public static class Toolkit
    {
        private static Random random = new Random();

        /**
         * Use this method to get the point of a specific element relative to the map
         * Usefull for figuring out coordinates of nested elements
         * */
        public static Point getPointRelativeToMap(UIElement element)
        {
            return element.TransformToAncestor(Map.INSTANCE).Transform(new Point(0, 0));
        }

        /**
         * Get a rect specifiying the current REAL bounds of the given element.
         * This method considers any and all transform applied to the element and return the resulting bounds.
         * */
        public static Rect getBounds(FrameworkElement element)
        {
            GeneralTransform transform = element.TransformToVisual(Map.INSTANCE);
            return transform.TransformBounds(new Rect(0, 0, element.ActualWidth, element.ActualHeight));
        }

        /**
         * Method to retrieve the centerPoint of a specific element
         * */
        public static Point getCenterPoint(FrameworkElement element)
        {
            Rect bounds = getBounds(element);
            return new Point((bounds.Left + bounds.Right) / 2, (bounds.Top + bounds.Bottom) / 2);
        }

        /**
          * Method to test if a certain elements width covers a specific X coordinate
          * */
        public static Boolean elementIsAtX(FrameworkElement element, double x)
        {
            Rect bounds = getBounds(element);
            return (bounds.Left <= x && bounds.Right >= x);
        }

        /**
          * Method to test if a certain elements heigth covers a specific Y coordinate
          * */
        public static Boolean elementIsAtY(FrameworkElement element, double y)
        {
            Rect bounds = getBounds(element);
            return (bounds.Top <= y && bounds.Bottom >= y);
        }

        /**
         * Use this method to find out if an element occurs at a specific point
         * */
        public static Boolean elementIsAtPoint(FrameworkElement element, Point p)
        {
            Point elementPoint = getPointRelativeToMap(element);
            return (p.X >= elementPoint.X && p.X <= (elementPoint.X + element.ActualWidth) && p.Y >= elementPoint.Y && p.Y <= (elementPoint.Y + element.ActualHeight));
        }

        public static Boolean elementIsAtPoint(Point elementPoint, FrameworkElement element, Point p)
        {
            return (p.X >= elementPoint.X && p.X <= (elementPoint.X + element.ActualWidth) && p.Y >= elementPoint.Y && p.Y <= (elementPoint.Y + element.ActualHeight));
        }

        public static Boolean elementIsAtPoint(Point elementPoint, double elementWidth, double elementHeight, Point p)
        {
            return (p.X >= elementPoint.X && p.X <= (elementPoint.X + elementWidth) && p.Y >= elementPoint.Y && p.Y <= (elementPoint.Y + elementHeight));
        }

        public static Boolean elementIsAtPoint(double elementX, double elementY, double elementWidth, double elementHeight, Point p)
        {
            return (p.X >= elementX && p.X <= (elementX + elementWidth) && p.Y >= elementY && p.Y <= (elementY + elementHeight));
        }

        /**
         * Use this method to grab a random element from a enumeration.
         * 
         * Example: Direction randomDirection = Toolkit.RandomEnum<Direction>();
         * */
        public static T RandomEnum<T>()
        {
            T[] values = (T[])Enum.GetValues(typeof(T));
            return values[random.Next(0, values.Length)];
        }

        /**
         * Use this method to safely add/remove degrees from a orientation (example: 270 + 180 becomes 90)
         * */
        public static int alterOrientationBy(int orientation, int modifier)
        {
            orientation += modifier;
            while (orientation >= 360)
                orientation -= 360;
            while (orientation < 0)
                orientation += 360;
            return orientation;
        }

        /**
         * Method to calculate the smallest difference between 2 Orientations (example: 270 - 1 = 91)
         * */
        public static int getOrientationDifference(int o1, int o2)
        {
            int bigO = (o1 > o2) ? o1 : o2;
            int smallO = (o1 > o2) ? o2 : o1;
            int result = bigO - smallO;
            if (result <= 180) return result;
            return (smallO + 360) - bigO;
        }

        /**
         * Method to convert Side to Orientation
         * */
        public static int sideToOrientation(Side side)
        {
            switch (side)
            {
                case Side.TOP: return 0;
                case Side.RIGHT: return 90;
                case Side.BOTTOM: return 180;
                case Side.LEFT: return 270;
                default: throw new InvalidOperationException("No such side exists");
            }
        }

        /**
         * Method to convert Orientation to Side
         * */
        public static Side orientationToSide(int orientation)
        {
            switch (orientation)
            {
                case 0: return Side.TOP;
                case 90: return Side.RIGHT;
                case 180: return Side.BOTTOM;
                case 270: return Side.LEFT;
                default: throw new InvalidOperationException("Invalid orientation");
            }
        }
    }
}
